# coding=utf-8

# 标题：龙头战法
# author：张连重   实盘版权所有,严禁外传
# version 3.0.0
#    date: 2021-02-14  21:47
#    description: 基于情绪周期实现  
#version  3.1.0
#    date: 2021-03-06  14:18
#    description:  取消集合竞价买入

#version  3.1.1
#    date: 2021-03-10  18:30
#    description:   1.解决情绪周期初始化问题
#                   2.当天已卖出的股票加入黑名单，不再买入



# 导入函数库
from gm.api import *
from datetime import datetime
import pandas as pd
from jqdatasdk import *
import numpy  as np
import time
import random
import math
import re
import os
from decimal import Decimal, Context, ROUND_HALF_UP,ROUND_DOWN





# jqdata login
auth('15520768082','XXXXX')


# 初始化函数，设定基准等等
def init(context):
    context.security = []                       #存放聚宽股票代码
    context.security_stock_juejin = []          #存放掘金股票代码
    context.security_observe = []               #待观察股票 
    context.sell_list = []
    context.small_cyc = 3
    context.small_cyc_befor = 3
    context.big_cyc = 3
    context.big_cyc_befor = 3
    context.summary_call_auction_askp1_data = {}      #集合竞价数据
    context.summary_call_auction_askbuy_volume_data = {}
    context.black_stock_code_list = ['000000','000000']
    context.black_stock_code_a = 0                  #交易股票代码黑名单,动态加入后，可支持手工操作 
    context.black_stock_code_b = 0                  #交易股票代码黑名单,动态加入后，可支持手工操作 
    context.limit_up_summary = pd.DataFrame(columns = ["股票代码掘金","股票代码聚宽","股票名称","昨日涨停高度","昨日收盘价"])              # 涨停数统计
    context.limit_up_summary.set_index(["股票代码掘金"], inplace=True)
    context.current_day_selled_stock_set = set()   #当日卖出股票列表
    context.emotion_cycle = pd.DataFrame({"big_cycle":[3,3,3,3,3,3,3,3,3,3],"small_cycle":[3,3,3,3,3,3,3,3,3,3],"date_limit_info":['','','','','','','','','','']})    # 情绪周期
    init_emotion_cycle(context)
    add_parameter(key='black_stock_code_a', value=context.black_stock_code_a, min=0, max=999999, name='black_stock_code_a', intro='股票黑名单A', group='stock_code', readonly=False)
    add_parameter(key='black_stock_code_b', value=context.black_stock_code_b, min=0, max=999999, name='black_stock_code_b', intro='股票黑名单B', group='stock_code', readonly=False)
    #add_parameter(key='small_cyc', value=context.small_cyc, min=1, max=20, name='samll cycle', intro='情绪小周期', group='emotioncyle', readonly=False)
    #add_parameter(key='small_cyc_befor', value=context.small_cyc_befor, min=1, max=20, name='small_cyc_befor', intro='情绪小周期-before', group='emotioncyle', readonly=False)
    #add_parameter(key='big_cyc', value=context.big_cyc, min=1, max=20, name='big cycle', intro='情绪大周期', group='emotioncyle', readonly=False)
    #add_parameter(key='big_cyc_befor', value=context.big_cyc_befor, min=1, max=20, name='big_cyc_befor', intro='情绪大周期-before', group='emotioncyle', readonly=False)
    

    # 开盘前运行
    schedule(schedule_func=before_market_open, date_rule='1d', time_rule='09:01:00')


    # 集合竞价尾期运行
    schedule(schedule_func=analyse_call_auction, date_rule='1d', time_rule='09:27:00')


    # 开盘时运行
    schedule(schedule_func=market_open, date_rule='1d', time_rule='09:30:00')
    
    # 收盘后运行
    schedule(schedule_func=after_market_close, date_rule='1d', time_rule='15:01:00')




# 响应参数修改
def on_parameter(context, parameter):

    #print(parameter)
    if parameter['key'] == 'black_stock_code_a':
        context.black_stock_code_a = math.trunc(parameter['value'])
        black_stock_code_a = '%06d' % context.black_stock_code_a
        context.black_stock_code_list[0] = fmt_code(black_stock_code_a, 'gm') 
        print('黑名单列表已修改为'+str(context.black_stock_code_list))
    if parameter['key'] == 'black_stock_code_b':
        context.black_stock_code_b = math.trunc(parameter['value']) 
        black_stock_code_b = '%06d' % context.black_stock_code_b
        context.black_stock_code_list[1] = fmt_code(black_stock_code_b, 'gm') 
        print('黑名单列表已修改为'+str(context.black_stock_code_list))
    """    
    if parameter['key'] == 'small_cyc':
        context.small_cyc = math.trunc(parameter['value']) 
        print('{}已修改为{}'.format(parameter['name'],context.small_cyc))
    if parameter['key'] == 'big_cyc':
        context.big_cyc = math.trunc(parameter['value']) 
        print('{}已修改为{}'.format(parameter['name'],context.big_cyc))
    if parameter['key'] == 'small_cyc_befor':
        context.small_cyc_befor = math.trunc(parameter['value']) 
        print('{}已修改为{}'.format(parameter['name'],context.small_cyc_befor))
    if parameter['key'] == 'big_cyc_befor':
        context.big_cyc_befor = math.trunc(parameter['value']) 
        print('{}已修改为{}'.format(parameter['name'],context.big_cyc_befor))  
    """         


## 盘中运行函数  
#  1、开盘后没封住，跌幅超过%5，盘中卖出
#  2、快收盘时，没封住涨停，盘中卖出
def on_bar(context, bars):
    order_in_marcket(context,bars)
    monitor_in_market(context,bars)
    monitor_before_close(context,bars) 


# 每个订阅的股票有数据，都会推送一次
def on_tick(context, tick):
   ##获取所订阅的股票集合竞价期间的tick数据
    current_time = context.now.strftime("%Y-%m-%d %H:%M:%S")
    today = context.now.strftime("%Y-%m-%d")
    if context.now.strftime("%H:%M:%S") >= '09:15:00' and  context.now.strftime("%H:%M:%S") <= '09:25:00':
        #统计集合竞价数据
        summary_call_auction(context,tick)

   
def init_emotion_cycle(context):
    """
    初始化情绪周期参数   T-3 DAY
    """ 
    today = context.now.strftime("%Y-%m-%d")
    trade_dates = get_trade_days(end_date=today, count=4)   
    do_specday_emotioncycle_init(context,trade_dates[0]) #T-3 
    do_specday_emotioncycle_init(context,trade_dates[1]) #T-2 
    emotion_cycle_init = {"big_cycle":context.big_cyc,"small_cycle":context.small_cyc}
    print("已自动完成情绪周期初始化"+str(emotion_cycle_init))


def do_specday_emotioncycle_init(context,spec_day):
    stocks = get_all_securities(types=['stock'], date=spec_day).index
    stocks = st_check(context,spec_day,stocks)
    stocks = filter_stks_listed_n_days(stocks,spec_day,100)
    df = get_price(stocks,end_date=spec_day,frequency='1d',fields=['open','close','high','high_limit','paused'], count=1, panel=False)
    df = df[df['paused']==0]    #未停牌
    df = df[df['close'] > 3.3]  # 收盘价高于3.3 不做低价股
    stocks = df[df['close']==df['high_limit']]['code'].tolist()  
    do_limit_up_summary(context,spec_day,stocks)
    print(spec_day)
    print(context.limit_up_summary)
    context.small_cyc = context.limit_up_summary['昨日涨停高度'].max()
    if context.small_cyc <= context.small_cyc_befor:
        context.big_cyc = context.small_cyc_befor
    elif context.small_cyc > context.big_cyc:
        context.big_cyc = context.small_cyc
    context.small_cyc_befor = context.small_cyc
    context.big_cyc_befor = context.big_cyc


def do_limit_up_summary(context,staticEndDate,stock_list):
    """
    获取连板统计
    """
    # 定义存放连续涨停的字典
    limit_up_dic = {}
    date_list = []
    code_list = []
    high_list = []
    name_list = []
    yesterday_close = []
    context.limit_up_summary.drop(context.limit_up_summary.index,inplace=True) #初始化
    for stock in stock_list:
        # 定义涨停次数(至少一次涨停，否则被过滤)
        n = 0
        df = get_price(stock,end_date=staticEndDate,frequency='1d',fields=['pre_close','low','close','high_limit','paused'] ,count=15 ,panel=False)
        #过滤当天停牌的股票
        if df['paused'][-1] != 0 or df['close'][-1] / df['pre_close'][-1] < 1.09:
           continue
        flag = False
        for i in range(15):
            if df['close'][-1-i] >= df['high_limit'][-1-i] and df['paused'][-1-i] == 0:
                 #昨日涨停
                if df['close'][-2-i] >= df['high_limit'][-2-i] and df['paused'][-2-i] == 0:
                    n += 1  #前日涨停板涨停，涨停数加1
                elif df['close'][-1-i] >= df['low'][-1-i] + 0.03:
                    n += 1  #昨日接近一字涨停
                else:
                    if df['close'][-i] >= df['low'][-i] + 0.03:
                        flag = True
                    else:
                        break
            else:
                if n > 0:
                    if df['close'][-i] >= df['low'][-i] + 0.03:
                        flag = True
                    elif df['close'][-i+1] >= df['low'][-i+1] + 0.03:
                        n = n -1
                        flag = True
            if flag == True:
                limit_up_dic[stock] = n
                code_list.append(stock)
                high_list.append(n)
                name_list.append(str(get_security_info(stock).display_name))
                yesterday_close.append(df['close'][-1])
                break
    context.limit_up_summary['股票代码掘金'] = list(transStockListCode(code_list,"JJ"))
    context.limit_up_summary['股票代码聚宽'] = list(code_list)
    context.limit_up_summary['股票名称'] = name_list
    context.limit_up_summary['昨日涨停高度'] = list(high_list)
    context.limit_up_summary['昨日收盘价'] = list(yesterday_close)
    context.limit_up_summary.set_index(["股票代码掘金"], inplace=True)
    context.limit_up_summary['昨日涨停高度'] = np.where(context.limit_up_summary['昨日涨停高度'] < -1, context.small_cyc, context.limit_up_summary['昨日涨停高度'])
    




def filter_stks_listed_n_days(source_stk_list, end_date, n=100):
    # type: (list, Union[str, datetime.date, datetime.datetime], int) -> list
    """
    过滤掉source_stk_list中尚未上市的，或者上市天数不足n天的股票
    """
    # 1. 取得n个交易日之前的交易日期trd_date
    trd_date = get_trade_days(end_date=end_date, count=n)[0]
    # 2. 取trd_date日就已经上市的所有股票
    all_stks = get_all_securities(date=trd_date)
    # 3. 过滤source_stk_list，剔除掉不在all_stks中的
    valid_stk_list = list(all_stks[all_stks.index.isin(source_stk_list)].index)
    return valid_stk_list


def st_check(context,date,security_list):
    """
    去除ST股票
    """
    current_data = get_extras('is_st', security_list.tolist(), end_date=date, df=True, count=1)   
    security_list = [stock for stock in security_list if not current_data[stock][-1]]
    # 返回结果
    return security_list



def statistical_limit_up(context,stock_list, staticEndDate):
    """
    统计连续涨停股票,获取今日股票池
    """
    limit_stocks = []
    do_limit_up_summary(context,staticEndDate,stock_list)
    context.small_cyc = context.limit_up_summary['昨日涨停高度'].max()
    df_data_max = context.limit_up_summary[context.limit_up_summary['昨日涨停高度'] == context.small_cyc]
    df_data_max_sec = context.limit_up_summary[context.limit_up_summary['昨日涨停高度'] == 3]
    print('------------新高股票---------'+str(staticEndDate)+'----')
    print(df_data_max)

    if context.small_cyc <= context.small_cyc_befor:
        context.big_cyc = context.small_cyc_befor
    elif context.small_cyc > context.big_cyc:
        context.big_cyc = context.small_cyc

   
    if context.big_cyc <= 4:
        if len(df_data_max) < 4:
            for stock in df_data_max['股票代码聚宽']:
                df = get_price(stock,end_date=staticEndDate,frequency='1d',fields=['volume','open','close','high_limit','money','paused'] ,count=10)
                volume_max10 = df['volume'].max()
                high_limit = df['high_limit'][-1]
                #自然板，加入买入列表
                if df['open'][-1] != df['high_limit'][-1]:
                    limit_stocks.append(stock)
                #一字板，前一个不能也是一字板 否则获利盘过多，容易追高接盘
                elif df['open'][-2] != df['high_limit'][-2]:
                    df_5m = get_price(stock,end_date=staticEndDate,frequency='5m',fields=['low'] ,count=12)
                    # 一字板最后一个小时拉涨停的，说明不强
                    low_12 = df_5m['low'].min()
                    if low_12 == high_limit:
                        limit_stocks.append(stock)
                        
    #大周期在6及以上，说明当前情绪不错，可以做weiyi3板的股  3板散户获利了结，留下庄家筹码继续拉高               
    elif context.big_cyc >= 6 and len(df_data_max_sec) == 1:
        for stock in df_data_max_sec['股票代码聚宽']:
            df = get_price(stock,end_date=staticEndDate,frequency='1d',fields=['volume','open','close','high_limit','money','paused'] ,count=10)
            #排除一字板
            if df['open'][-1] != df['high_limit'][-1]:
                limit_stocks.append(stock)  
       
    context.security = limit_stocks
    context.security_stock_juejin = transStockListCode(limit_stocks,'JJ')
    context.small_cyc_befor = context.small_cyc
    context.big_cyc_befor = context.big_cyc

    
def pick_stocks(context):
    """
    获得候选股票池  剔除黑名单股票
    """
    staticEndDate = context.now.strftime("%Y-%m-%d")
    last_date = get_trade_days(end_date=staticEndDate, count=2)[0]
    stocks = get_all_securities(types=['stock'], date=staticEndDate).index
    stocks = st_check(context,last_date,stocks)
    stocks = filter_stks_listed_n_days(stocks,staticEndDate,100)
    
    df = get_price(stocks,end_date=last_date,frequency='1d',fields=['open','close','high','high_limit','paused'], count=1, panel=False)
    df = df[df['paused']==0]    #未停牌
    df = df[df['close'] > 3.3]  # 收盘价高于3.3
    stocks = df[df['close']==df['high_limit']]['code'].tolist()   
    return stocks




 ##
 # 聚宽和掘金股票代码相互转换
 #        上交所       深交所    浦发银行  
 # 掘金   SHSE         SZSE    SHSE.600000	
 # 聚宽   XSHG 	       XSHE    600000.XSHG  	
 ## 		


def transStockCode(stock_src,target_type):
    """
    单股名称转换
    """
    if target_type=='JJ':
       target = 'SHSE' if stock_src[7:]=='XSHG' else 'SZSE'
       return target +'.'+ stock_src[0:6]
    elif  target_type=='JK':
       target = 'XSHG' if stock_src[0:4]=='SHSE' else 'XSHE'
       return stock_src[5:] +'.'+ target    



def transStockListCode(stock_list_src,target_type):
    """
    股票列表名称转换
    """
    stock_list_target = []
    if target_type=='JJ':
        for stock in stock_list_src:
            target = 'SHSE' if stock[7:]=='XSHG' else 'SZSE'
            stock_list_target.append(target +'.'+ stock[0:6])
    elif  target_type=='JK':
       for stock in stock_list_src:
            target = 'XSHG' if stock[0:4]=='SHSE' else 'XSHE'
            stock_list_target.append(stock[5:] +'.'+ target)   
    return stock_list_target        

def fmt_code(code: str, style: str = None):  
    """
    对股票代码格式化处理
    ---
    :param code: 股票代码
    :param style: 代码风格
    """
    pattern = re.compile(r"\d+")
    code = pattern.findall(code)[0]
    if style in ["jq", "joinquant", "聚宽"]:
        return code + ".XSHG" if code[0] == "6" else code + ".XSHE"
    if style in ["wd", "windcode", "万得"]:
        return code + ".SH" if code[0] == "6" else code + ".SZ"
    if style in ["gm", "goldminer", "掘金"]:
        return "SHSE." + code if code[0] == "6" else "SZSE." + code
    if style in ["ss", "skysoft", "天软"]:
        return "SH" + code if code[0] == "6" else "SZ" + code
    if style in ["ts", "tushare", "挖地兔"]:
        return code + ".SH" if code[0] == "6" else code + ".SZ"
    else:
        return code    


def before_market_open(context):
    """
    开盘前运行函数,获取连板目标股票列表
    """
    # 输出运行时间
    log(level='info', msg='********新的一天开始**********before_market_open '+str(context.now), source='TrackLeader')
    print("******新的一天开始****before_market_open "+str(context.now))
    # 获得候选股票
    stock_list = pick_stocks(context)
    staticEndDate = context.now.strftime("%Y-%m-%d")
    last_date = get_trade_days(end_date=staticEndDate, count=2)[0]
    # 统计昨天涨停股票在过去9天内的连扳情况
    statistical_limit_up(context,stock_list,last_date)
    
    ## 开盘确定要订阅的股票池  
    subscribe_stocks = list(set(context.security_stock_juejin + get_position_list(context)))
    subscribe(subscribe_stocks, frequency='tick', count=1, wait_group=False, wait_group_timeout='10s', unsubscribe_previous=False)
    subscribe(subscribe_stocks, frequency='60s', count=2, wait_group=False, wait_group_timeout='10s', unsubscribe_previous=False)
    #初始化交易数据
    context.sell_list = []
    context.limit_stocks = []
    context.buy_list_temp = []
    context.summary_call_auction_askp1_data = {}
    context.summary_call_auction_askbuy_volume_data = {}
    context.call_auction_orderedlist = []
    context.org_tick = {}      # 原始tick数据
    context.smoothed_tick = {} #平滑过后的tick数据
    context.current_day_selled_stock_set.clear()



def summary_call_auction(context,tick):
    """
    统计集合竞价盘口数据 
    dict {"symbol",[ask_p_0,ask_p_1,ask_p_2,...,ask_p_n]}
    """
    context.summary_call_auction_askp1_data.setdefault(tick['symbol'],[])
    context.summary_call_auction_askp1_data[tick['symbol']].append(tick['quotes'][0]['ask_p']) 
    buy_sell_volumn = [tick['quotes'][0]['bid_v']+tick['quotes'][1]['bid_v']+tick['quotes'][2]['bid_v'],tick['quotes'][0]['ask_v']+tick['quotes'][1]['ask_v']+tick['quotes'][2]['ask_v']]
    context.summary_call_auction_askbuy_volume_data[tick['symbol']] = buy_sell_volumn



def analyse_call_auction(context):
    """
    分析集合竞价情况
    """
    if len(context.summary_call_auction_askp1_data) ==0 :
        return    
    security_observe = []
    buy_list_temp = []
    call_auction_orderlist = []
    today = context.now.strftime("%Y-%m-%d")
    last_date = get_trade_days(end_date=today, count=2)[0]
    call_auction_index = context.summary_call_auction_askp1_data.keys()
    for stock_code in context.security_stock_juejin: 
       #进行key值检测，防止股票当日停牌 
       if stock_code not in call_auction_index:
          continue
       level_data = context.summary_call_auction_askp1_data[stock_code]
       if len(level_data) > 0:
          a1_p_max = max(level_data)
          a1_p_min = min(level_data)
          a1_p0 = level_data[-1]  
          a1_p1 = level_data[-2]  
          a1_p2 = level_data[-3]  
         # 获取股票前N日的收盘价
          stock_code_jk = transStockCode(stock_code,'JK') 
          close_data_1d = get_bars(stock_code_jk, count=3, unit='1d', fields=['open','high','low','close','volume'],end_dt=today)  
          close_1 = close_data_1d['close'].iloc[-1]
          close_3 = close_data_1d['close'].iloc[-3]
          if a1_p0 / close_3 < 1.5 and a1_p0 / close_1 > 0.95 and  (close_data_1d['volume'].iloc[-1] / close_data_1d['volume'].iloc[-3] < 3 or a1_p0 / close_1 < 1.056):
              if a1_p0 < a1_p1 or a1_p1 < a1_p2:
                  security_observe.append(stock_code)
              else:
                  buy_list_temp.append(stock_code)
    context.security_stock_juejin = buy_list_temp
    context.security = transStockListCode(buy_list_temp,'JK')
    context.security_observe = security_observe 
    stock_opration = {"今日青睐股列表":context.security_stock_juejin,"今日观察股列表":context.security_observe}





def market_open(context):
    """
    开盘时运行函数
    """
    #----------------------------------------------------------------------
    today = context.now.strftime("%Y-%m-%d")
    # 查询默认交易账户所有持仓
    hold_stocks = get_all_position(context)
    print("今日集合竞价统计数据:"+str(context.summary_call_auction_askp1_data))
    print("今日集合竞价买卖方volumn数据:"+str(context.summary_call_auction_askbuy_volume_data))

    ##分析是否需要卖出现有持仓
    for stock in hold_stocks:
        # 获取股票的收盘价
        stock_jk = transStockCode(stock,'JK') 
        close_data = get_bars(stock_jk, count=3, unit='1d', fields=['open','high','low','close'],end_dt=today)
        close_1 = close_data['close'].iloc[-1]
        close_2 = close_data['close'].iloc[-2] 
        if close_1/close_2 < 1.098:
            level_data = context.summary_call_auction_askp1_data[stock]
            if len(level_data) > 0:
                a1_p_max = max(level_data)
                a1_p_min = min(level_data)
                a1_p0 = level_data[-1]   
                a1_p1 = level_data[-2]  
                a1_p2 = level_data[-3]    
                if a1_p0 < a1_p1 or a1_p1 < a1_p2 or a1_p0 / close_1 < 0.98:
                    result = custom_order_target_volume(context,symbol=stock, volume=0, position_side=PositionSide_Long, order_type=OrderType_Market)  #开盘市价清仓 
                    context.sell_list.append(stock)
                    log(level='info', msg='昨天收盘没涨停，今日集合竞价没直接封住，开盘卖出'+str(stock), source='TrackLeader')
                    print('昨天收盘没涨停，今日集合竞价没直接封住，开盘卖出'+str(stock))
                    print(result)
                    context.current_day_selled_stock_set.add(stock)
        elif close_data['close'].iloc[-1] == close_data['low'].iloc[-1] and close_data['close'].iloc[-2] == close_data['low'].iloc[-2] :
            result = custom_order_target_volume(context,symbol=stock, volume=0, position_side=PositionSide_Long, order_type=OrderType_Market)  #开盘市价清仓 
            print('连续两天一字板，大部分筹码盈利过高，开盘卖出'+str(stock))   
            print(result)


def get_all_position(context):
    """
    获取当前所有持仓股票
    """
    in_stock = []
    Account_positions = context.account().positions()
    for position in Account_positions:
        in_stock.append(position['symbol'])
    return in_stock    

def smoothTickData(context,tick):
    """
    基于移动平均平滑tick信号 yn
    """
    context.org_tick.setdefault(tick['symbol'],[0.0,0.0,0.0,0.0,0.0,''])      # 原始tick数据
    context.org_tick[tick['symbol']].pop(0)      
    context.org_tick[tick['symbol']].append(tick['price'])  
    context.smoothed_tick.setdefault(tick['symbol'],0.0)
    smooth_value = np.mean(context.org_tick[tick['symbol']])     
    context.smoothed_tick[tick['symbol']] = smooth_value   #平滑过后的tick数据


def get_in_stock(context):
    """
    获取当前持仓情况 股票代码  股票名称 总持仓 今日持仓 可用持仓 成本价 收益值
    """
    detail_instock = pd.DataFrame(columns = ["股票代码","股票名称","总持仓",'今日持仓','可用持仓',"成本价"])
    stock_name = []
    stock_code =[]
    total_count = []
    today_count = []
    active_count = []
    cost = []
    Account_positions = context.account().positions()
    for position in Account_positions:
        stock_code.append(position['symbol'])
        name = str(get_security_info(transStockCode(position['symbol'],'JK')).display_name)
        stock_name.append(name)
        total_count.append(position['volume'])
        today_count.append(position['volume_today'])
        active_count.append(position['volume']-position['volume_today'])
        cost.append(position['vwap'])
      
    detail_instock['股票代码'] = list(stock_code)
    detail_instock['股票名称'] = list(stock_name)
    detail_instock['总持仓'] = list(total_count)
    detail_instock['今日持仓'] = list(today_count)
    detail_instock['可用持仓'] = list(active_count)
    detail_instock['成本价'] = list(cost)
    detail_instock.set_index(["股票代码"], inplace=True)
    return detail_instock


def get_position_list(context):
    """"
    获取持仓股票列表
    """
    in_stock = []
    Account_positions = context.account().positions()
    for position in Account_positions:
        in_stock.append(position['symbol'])
    return in_stock    


def get_fundamental_info(stock_code,queryDate):
    """
    获取股票基本面信息
    """
    q = query(
          valuation.code,                             #股票代码	带后缀.XSHE/.XSHG
          valuation.capitalization,                   #总股本(万股)
          valuation.circulating_cap,                  #流通股本(万股) 
          valuation.market_cap,                       #总市值(亿元)     
          valuation.circulating_market_cap,           #流通市值(亿元)     
          valuation.turnover_ratio,                   #换手率(%)    
          valuation.pe_ratio_lyr,                     #市盈率(PE)     
          valuation.pb_ratio                          #市净率(PB)	     
      ).filter(
          valuation.code == stock_code,
      )
    result = get_fundamentals(q, date=queryDate)
    return result


def order_in_marcket(context,bars):
    """
    盘中下单
    """
    # 09:30:05 ~ 09:45:00,购买青睐股及待观察股  
    current_time = context.now.strftime("%Y-%m-%d %H:%M:%S")
    today = context.now.strftime("%Y-%m-%d")
    if context.now.strftime("%H:%M:%S") >= '09:30:05' and  context.now.strftime("%H:%M:%S") <= '09:45:00':
        for bar in bars:
           stock = bar['symbol']
           if stock in get_position_list(context):
               continue 
           if stock in context.current_day_selled_stock_set:
               continue    
           # 购买青睐股
           if len(context.security_stock_juejin) != 0 :
               cash = context.account().cash['available']
               total_amount = context.account().cash['nav']
               if cash/total_amount >= 0.2:
                   print("市价买入青睐股 %s" % (stock))
                   buy_cash = cash/len(context.security_stock_juejin)
                   result = custom_order_assign_value(context,symbol=stock, value=buy_cash, order_type=OrderType_Market)
                   print(result)
          
           # 购买待观察股
           if len(context.security_observe) == 0:
               continue
           cash = context.account().cash['available']
           total_amount = context.account().cash['nav']
           if cash/total_amount >= 0.25:
               #可用资金大于总资金25%,才购买待观察股，最多持仓3只股
              buy_cash = cash/len(context.security_observe)
              if stock in context.security_observe:
                   level_data = context.summary_call_auction_askp1_data[stock]
                   a1_p_min = min(level_data[-1*len(level_data)//2:])
                   if bar['close'] > a1_p_min :
                       log(level='info', msg="市价买入待观察股 %s" % (stock), source='TrackLeader')
                       print("市价买入待观察股 %s" % (stock))
                       result = custom_order_assign_value(context,symbol=stock, value=buy_cash, order_type=OrderType_Market)
                       if result != None and result[0]['ord_rej_reason'] == 0:
                          context.security_observe.remove(stock)  #买入成功后从列表删除
                       print(result)


def monitor_in_market(context,bars):
    """
    监听盘中情况
    """
    today = context.now.strftime("%Y-%m-%d")
    for bar in bars:
        stock = bar['symbol']
        if stock in get_all_position(context):
          stop_line = 0.92
          # 获取股票的收盘价
          stock_jk = transStockCode(stock,'JK')
          close_data_1d = get_bars(stock_jk, count=3, unit='1d', fields=['open','high','low','close'],end_dt=today)
  
          #如果已连涨3天
          if close_data_1d['close'].iloc[-1]/close_data_1d['close'].iloc[-2] > 1.12:
              stop_line = 0.85
              
          if bar['close']/close_data_1d['close'].iloc[-1] < stop_line:
              instock_detail = get_in_stock(context)
              active_count = instock_detail.loc[stock]['可用持仓']
              if active_count > 0:
                 result = custom_order_volume(context,symbol=stock, volume=active_count, side=OrderSide_Sell, order_type=OrderType_Market, position_effect=PositionEffect_Close)
                 context.sell_list.append(stock)
                 print('跌幅超过8%，盘中卖出'+str(stock)+' '+str(active_count)+'股')
                 print(result)
                 context.current_day_selled_stock_set.add(stock)

     

def monitor_before_close(context,bars):
    """
    监听尾盘情况    
    """
    current_time = context.now.strftime("%H:%M:%S")
    today = context.now.strftime("%Y-%m-%d")
    if context.now.strftime("%H:%M:%S") >= '14:55:00' and  context.now.strftime("%H:%M:%S") <= '15:00:00':
        instock_detail = get_in_stock(context)
        for bar in bars:
           stock = bar['symbol']
           if stock in get_all_position(context): 
               active_count = instock_detail.loc[stock]['可用持仓']
               if active_count > 0: 
                   stock_jk = transStockCode(stock,'JK')
                   close_data_yesterday = get_bars(stock_jk, count=1, unit='1d', fields=['open','high','low','close'],end_dt=today) 
                   close_price_yesterday = close_data_yesterday['close'].iloc[-1]
                   current_close_price = bar['close'] 
                   if current_close_price/close_price_yesterday< 1.098:
                       print('尾盘没有涨停，市价清空可用持仓'+str(stock)+ ' '+str(active_count)+'股')
                       log(level='info', msg='尾盘没有涨停，市价清空可用持仓'+str(stock)+' '+str(active_count)+'股', source='TrackLeader')
                       result = custom_order_volume(context,symbol=stock, volume=active_count, side=OrderSide_Sell, order_type=OrderType_Market, position_effect=PositionEffect_Close)
                       print(result) 
                       context.current_day_selled_stock_set.add(stock)
                   elif current_close_price/close_price_yesterday> 1.11 and current_close_price/close_price_yesterday < 1.195:
                       log(level='info', msg='尾盘涨停，相比昨日剧烈收盘,波动过大，市价清空可用持仓'+str(stock)+ ' '+str(active_count)+'股', source='TrackLeader')
                       print('尾盘涨停，相比昨日剧烈收盘,波动过大，市价清空可用持仓'+str(stock)+ ''+str(active_count)+'股')
                       result = custom_order_volume(context,symbol=stock, volume=active_count, side=OrderSide_Sell, order_type=OrderType_Market, position_effect=PositionEffect_Close)
                       print(result) 
                       context.current_day_selled_stock_set.add(stock)
                   else:
                       log(level='info', msg='---尾盘涨停不卖出--，stock_code = '+str(stock), source='TrackLeader')
                       print('---尾盘涨停不卖出--，stock_code = '+str(stock))

                       
               
def after_market_close(context):
    """
    收盘后运行函数
    """
    ## 收盘取消所有订阅的数据
    log(level='info', msg='盘后运行after_market_close:'+str(context.now), source='TrackLeader')
    #取消订阅
    unsubscribe(symbols='*', frequency='tick')
    unsubscribe(symbols='*', frequency='60s')
    #当前持仓统计
    print(get_in_stock(context))
    log(level='info', msg='一天结束', source='TrackLeader')
    # send_message('美好的一天~')
    log(level='info', msg='##############################################################', source='TrackLeader')
    print('##############################################################')


def custom_order_volume(context,symbol, volume, side, order_type, position_effect,
                 price=0, order_duration=OrderDuration_Unknown, order_qualifier=OrderQualifier_Unknown, account=''):
    """
    按定量委托
    """         
    if symbol in  context.black_stock_code_list :
        return None     
    return  order_volume(symbol, volume, side, order_type, position_effect,price, order_duration, order_qualifier, account)           

def custom_order_percent(context,symbol, percent, side, order_type, position_effect,
                  price=0, order_duration=OrderDuration_Unknown, order_qualifier=OrderQualifier_Unknown, account=''):
    # type: (Text, float, int, int, int, float, int, int, Text)->List[Dict[Text, Any]]
    """
    按指定比例委托
    """
    if symbol in  context.black_stock_code_list :
        return None 
    return  order_percent(symbol, percent, side, order_type, position_effect,price, order_duration, order_qualifier, account)

def custom_order_value(context,symbol, value, side, order_type, position_effect,
                price=0, order_duration=OrderDuration_Unknown, order_qualifier=OrderQualifier_Unknown, account=''):
    # type:(Text, float, int, int, int, float, int, int, Text) ->List[Dict[Text, Any]]
    """
    按指定价值委托
    """
    if symbol in  context.black_stock_code_list :
        return None 
    return order_value(symbol, value, side, order_type, position_effect,price, order_duration, order_qualifie, account)


def custom_order_assign_value(context,symbol, value,order_type, price=0):
    # type: (Text, float, int, int, float, int, int, Text) ->List[Dict[Text, Any]]
    """
    指定金额买入,考虑到实盘时 券商以涨停价冻结资金，买入场景下转换为指定股数买入
    """
    if symbol in  context.black_stock_code_list :
        return None 
    if order_type == OrderType_Market:
       # 从context中提取昨日收盘价，计算可买入股数
       order_limit_price_temp = str(context.limit_up_summary.at[symbol,'昨日收盘价']*1.10)
       order_limit_price =float(str(Decimal(order_limit_price_temp).quantize(Decimal('0.00'), ROUND_HALF_UP)))
       target_volumn = int(value/order_limit_price)//100 *100
       if target_volumn >= 100: 
          return  order_volume(symbol, target_volumn, side = PositionSide_Long, order_type = OrderType_Market, position_effect = PositionEffect_Open)           
       else:
          return None
    elif order_type == OrderType_Limit:
       target_volumn = int(value/price)
       if target_volumn >= 100: 
          return  order_volume(symbol, target_volumn, side = PositionSide_Long, order_type = OrderType_Limit, position_effect = PositionEffect_Open,price = price)           
       else:
          return None


def custom_order_target_percent(context,symbol, percent, position_side, order_type, price=0, order_duration=OrderDuration_Unknown,
                         order_qualifier=OrderQualifier_Unknown, account=''):
    # type: (Text, float, int, int, float, int, int, Text) ->List[Dict[Text, Any]]
    """
    调仓到目标持仓比例
    """
    if symbol in  context.black_stock_code_list :
        return None 
    return order_target_percent(symbol, percent, position_side, order_type, price, order_duration, order_qualifier, account)   


def custom_order_target_volume(context,symbol, volume, position_side, order_type, price=0, order_duration=OrderDuration_Unknown,
                        order_qualifier=OrderQualifier_Unknown, account=''):
    """
    调仓到目标持仓量
    """  
    if symbol in  context.black_stock_code_list :
        return None 
    return order_target_volume(symbol, volume, position_side, order_type, price, order_duration, order_qualifier, account)                  

if __name__ == '__main__':
    '''
    strategy_id策略ID,由系统生成
    filename文件名,请与本文件名保持一致
    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST
    token绑定计算机的ID,可在系统设置-密钥管理中生成
    backtest_start_time回测开始时间
    backtest_end_time回测结束时间
    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
    backtest_initial_cash回测初始资金
    backtest_commission_ratio回测佣金比例
    backtest_slippage_ratio回测滑点比例
    '''
    run(strategy_id='46674e89-75c6-11eb-a614-5254009e6375',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='45f8e34a0dacdbbbba2f2cab37b9c01772f80c53',
        backtest_start_time='2021-02-01 08:00:00',
        backtest_end_time='2021-04-29 09:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=50000,
        backtest_commission_ratio=0.00025,
        backtest_slippage_ratio=0.0001)   
